﻿using System.IO;
using System.Text;
using System.Windows;

using System.Reflection;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
using Windows.Security.Cryptography.Core;

namespace WindowsPhoneRT
{
    /// <summary>
    /// The encryption namespace isn't available for PCLs.  In this case, create your own Encryptor
    /// class using the IEncryptor interface and set it into the FileDb object via SetEncryptor.
    /// 
    /// This is the same implementation used in the FileDb DLL (Windows platform only)
    /// </summary>
    /// 
    public class MyEncryptor : FileDbNs.IEncryptor
    {
        CryptographicKey _derivedKey,
                         _symmKey;

        IBuffer _saltMaterial;

        /// <summary>
        /// Constructor taking a key (password) and salt as a string
        /// </summary>
        /// <param name="encryptionKey"></param>
        /// <param name="salt"></param>
        /// 
        public MyEncryptor( string encryptionKey, string salt )
        {
            IBuffer pwBuffer = CryptographicBuffer.ConvertStringToBinary( encryptionKey, BinaryStringEncoding.Utf8 );
            IBuffer saltBuffer = CryptographicBuffer.ConvertStringToBinary( salt, BinaryStringEncoding.Utf16LE );
//            IBuffer plainBuffer = CryptographicBuffer.ConvertStringToBinary( plainText, BinaryStringEncoding.Utf16LE );

            // Derive key material for password size 32 bytes for AES256 algorithm
            KeyDerivationAlgorithmProvider keyDerivationProvider = Windows.Security.Cryptography.Core.KeyDerivationAlgorithmProvider.OpenAlgorithm( "PBKDF2_SHA1" );
            // using salt and 1000 iterations
            KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2( saltBuffer, 1000 );

            // create a key based on original key and derivation parmaters
            CryptographicKey keyOriginal = keyDerivationProvider.CreateKey( pwBuffer );
            IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial( keyOriginal, pbkdf2Parms, 32 );
            _derivedKey = keyDerivationProvider.CreateKey( pwBuffer );

            // derive buffer to be used for encryption salt from derived password key
            _saltMaterial = CryptographicEngine.DeriveKeyMaterial( _derivedKey, pbkdf2Parms, 16 );

            // display the buffers – because KeyDerivationProvider always gets cleared after each use, they are very similar unforunately
//            string keyMaterialString = CryptographicBuffer.EncodeToBase64String( keyMaterial );
//            string saltMaterialString = CryptographicBuffer.EncodeToBase64String( saltMaterial );

            SymmetricKeyAlgorithmProvider symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm( "AES_CBC_PKCS7" );
            // create symmetric key from derived password key
            _symmKey = symProvider.CreateSymmetricKey( keyMaterial );
      
        }
        
        /// <summary>
        /// Encrypt the passed byte array
        /// </summary>
        /// <param name="dataToEncrypt">The data to encrypt</param>
        /// <returns>The encrypted data</returns>
        /// 
        public byte[] Encrypt( byte[] dataToEncrypt )
        {
            var iBuff = CryptographicBuffer.CreateFromByteArray( dataToEncrypt );
            // encrypt data buffer using symmetric key and derived salt material
            IBuffer resultBuffer = CryptographicEngine.Encrypt( _symmKey, iBuff, _saltMaterial );
            byte[] result;
            CryptographicBuffer.CopyToByteArray( resultBuffer, out result );

            return result;
        }

        /// <summary>
        /// Decrypt the passed byte array
        /// </summary>
        /// <param name="encryptedData">The data to decrypt</param>
        /// <returns>The decrypted data</returns>
        /// 
        public byte[] Decrypt( byte[] encryptedData )
        {
            //IBuffer pwBuffer = CryptographicBuffer.ConvertStringToBinary( pw, BinaryStringEncoding.Utf8 );
            //IBuffer saltBuffer = CryptographicBuffer.ConvertStringToBinary( salt, BinaryStringEncoding.Utf16LE );
            //IBuffer cipherBuffer = CryptographicBuffer.CreateFromByteArray( encryptedData );

            /*
            // Derive key material for password size 32 bytes for AES256 algorithm
            KeyDerivationAlgorithmProvider keyDerivationProvider = Windows.Security.Cryptography.Core.KeyDerivationAlgorithmProvider.OpenAlgorithm( "PBKDF2_SHA1" );
            // using salt and 1000 iterations
            KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2( saltBuffer, 1000 );

            // create a key based on original key and derivation parmaters
            CryptographicKey keyOriginal = keyDerivationProvider.CreateKey( pwBuffer );
            IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial( keyOriginal, pbkdf2Parms, 32 );
            CryptographicKey derivedPwKey = keyDerivationProvider.CreateKey( pwBuffer );

            // derive buffer to be used for encryption salt from derived password key
            IBuffer saltMaterial = CryptographicEngine.DeriveKeyMaterial( derivedPwKey, pbkdf2Parms, 16 );

            // display the keys – because KeyDerivationProvider always gets cleared after each use, they are very similar unforunately
            //string keyMaterialString = CryptographicBuffer.EncodeToBase64String( keyMaterial );
            //string saltMaterialString = CryptographicBuffer.EncodeToBase64String( saltMaterial );

            SymmetricKeyAlgorithmProvider symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm( "AES_CBC_PKCS7" );
            // create symmetric key from derived password material
            CryptographicKey symmKey = symProvider.CreateSymmetricKey( keyMaterial );
            */

            var iBuff = CryptographicBuffer.CreateFromByteArray( encryptedData );

            // encrypt data buffer using symmetric key and derived salt material
            IBuffer resultBuffer = CryptographicEngine.Decrypt( _symmKey, iBuff, _saltMaterial );
            byte[] result;
            CryptographicBuffer.CopyToByteArray( resultBuffer, out result );
            return result;
        }
    }
}
